package dataStructure.graph;

/**
 * 最小生成树
 */
public class Prim {
    private static final int INF = Integer.MAX_VALUE;

    /**
     * 初始化距离矩阵
     *
     * @param graph 图
     * @return 距离矩阵
     */
    private static int[][] initDistance(GraphMatrix graph) {
        int vertexNum = graph.getVertexNum();
        int[][] result = new int[vertexNum][vertexNum];
        // 初始化距离矩阵
        for (int i = 0; i < vertexNum; i++) {
            for (int j = 0; j < vertexNum; j++) {
                result[i][j] = graph.edges[i][j];
                if (i != j && result[i][j] == 0) {
                    result[i][j] = INF;
                }
            }
        }
        return result;
    }

    private static int prim(GraphMatrix graph) {
        int vertexNum = graph.getVertexNum();
        //统计最小的权
        int sum = 0;
        //当前最小生成树所能到达的顶点的最小权数组
        int[] costs = new int[vertexNum];
        //当前各个顶点对应的起点
        int[] startPoint = new int[vertexNum];
        int[][] distance = initDistance(graph);
        //初始化
        for (int i = 1; i < vertexNum; i++) {
            //所有点的起点赋值为0
            startPoint[i] = 0;
            //以0为起点到达各个顶点的权值
            costs[i] = distance[0][i];
        }
        //挑选剩余的vertexNum-1个顶点
        for (int i = 1; i < vertexNum; i++) {
            System.out.print("cost:");
            for (int cost : costs) {
                System.out.print(cost + " ");
            }
            System.out.println();
            //记录当前costs里面的最小权值是多少
            int min = INF;
            //记录当前costs里面的最小权值对应的数组下标,即顶点
            //(数组[顶点]=该顶点对应的起点)
            int minIndex = 0;
            //遍历costs 找出距离当前集合权值最小的点
            for (int j = 1; j < vertexNum; j++) {
                int temp = costs[j];
                //costs[j]==0代表节点j已加入MST
                if (temp != 0 && temp < min) {
                    min = temp;
                    minIndex = j;
                }
            }
            sum += min;
            //将已加入MST的对应的权值赋值为0
            costs[minIndex] = 0;
            //选定了新的顶点到MST后，树到达各顶点的最小开销和起点将更新
            //更新costs和startPoint
            for (int k = 0; k < vertexNum; k++) {
                //用minIndex顶点到各个顶点的权值比较costs数组的值，若较小则替换，并更新起点为minIndex
                int newCost = distance[minIndex][k];
                if (newCost != 0 && newCost < costs[k]) {
                    costs[k] = newCost;
                    //更新K的起点为minIndex
                    startPoint[k] = minIndex;
                }
            }
        }
        System.out.println("树的边为:");
        for (int i = 1; i < vertexNum; i++) {
            System.out.println(graph.vertexList.get(i) + "->" + graph.vertexList.get(startPoint[i]));
        }
        return sum;
    }

    public static void main(String[] args) {
        GraphMatrix<String> graph = new GraphMatrix<>("ABCDEF");
        graph.addEdge("A", "B", 6);
        graph.addEdge("A", "C", 1);
        graph.addEdge("A", "D", 5);
        graph.addEdge("B", "C", 5);
        graph.addEdge("B", "E", 3);
        graph.addEdge("C", "D", 5);
        graph.addEdge("C", "E", 6);
        graph.addEdge("C", "F", 4);
        graph.addEdge("D", "F", 2);
        graph.addEdge("E", "F", 6);
        graph.information();
        System.out.println("最小生成树权重和：" + prim(graph));
    }
}
